package org.vyana.model.vo {

	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	
	import mx.events.ModuleEvent;
	import mx.modules.IModuleInfo;
	import mx.modules.ModuleManager;
	import mx.rpc.Fault;
	import mx.rpc.events.FaultEvent;
	
	import org.vyana.model.modules.*;

	public class VyanaModuleDescriptor extends VyanaValueObject {

		/**
		 * Denotes if this module has to be loaded instantly
		 */
		public var isDefault:Boolean;

		/**
		 * URL pointing to module swf file 
		 */
		public var url:String;
		
		/**
		 * Module Id, must be distinct for each module type 
		 */
		public var id:String;
		
		/**
		 * Module type, resources, font, theme, etc. 
		 */
		public var type:String;

		/**
		 * Fires when module is ready to be used 
		 */
		public var moduleReadyFunction:Function;		

		public var moduleInfo:IModuleInfo;
		public var module:VyanaModule;

		protected var loader:URLLoader;

		private var _isLoadingInProgress:Boolean;
		private var _internalModuleClass:Class;

		function VyanaModuleDescriptor(type:String, id:String, url:String, isDefault:Boolean = false) {
			this.id = id;
			this.type = type;
			this.url = url;
			this.isDefault = isDefault;
		}

		public function get internalModuleClass():Class {
			return _internalModuleClass;
		}

		public function set internalModuleClass(value:Class):void {
			_internalModuleClass = value;
			onModuleReady();
		}
		
		public function get isLoaded():Boolean {
			return module != null;
		}

		public function get isLoadingInProgress():Boolean {
			return _isLoadingInProgress;
		}

		public function set isLoadingInProgress(value:Boolean):void {
			_isLoadingInProgress = value;
		}

		[Transient]
		public function get label():String {
			return url;
		}

		override public function set xml(value:XML):void {
			super.xml = value;

			url = String(value.@url);
		}

		public function unload():void {
			if (!isLoaded)
				return;

			moduleInfo.unload();
			moduleInfo = null;
			module.unload();
			module = null;
		}

		public function load(moduleBytes:ByteArray = null):void {

			// Module is internal nothing to load actually
			if (!url)
				return;
			
			if (isLoaded || isLoadingInProgress)
				return;

			new VyanaModuleEvent(VyanaModuleEvent.DOWNLOAD_STARTED, this).dispatch();
			isLoadingInProgress = true;
			
			if (moduleBytes) {				
				loadModuleBytes(moduleBytes);
				return;
			}

			loader = new URLLoader(new URLRequest(url));
			loader.dataFormat = URLLoaderDataFormat.BINARY;
			loader.addEventListener(IOErrorEvent.IO_ERROR, onModuleIOError);
			loader.addEventListener(Event.COMPLETE, onModuleBytesLoaded);
			loader.addEventListener(ProgressEvent.PROGRESS, onModuleLoadingProgress);
		}

		protected function onModuleBytesLoaded(e:Event):void {
			loadModuleBytes(ByteArray(URLLoader(e.target).data));
		}

		protected function loadModuleBytes(moduleBytes:ByteArray):void {
			moduleInfo = ModuleManager.getModule(url);
			with (moduleInfo) {
				addEventListener(ModuleEvent.READY, onModuleReady);
				addEventListener(ModuleEvent.ERROR, onModuleError);
				load(null, null, moduleBytes);
			}

			if (loader) {
				loader.removeEventListener(Event.COMPLETE, onModuleBytesLoaded);
				loader = null;
			}
		}

		public function newModuleInstance():VyanaModule {

			var m:VyanaModule;
			if (internalModuleClass)
				m = new internalModuleClass() as VyanaModule
			else {
				if (!moduleInfo)
					return null;
				
				m = moduleInfo.factory.create() as VyanaModule;
			}	
				
			if (m)
				m.moduleDescriptor = this;

			return m;
		}

		protected function onModuleReady(e:ModuleEvent = null):void {

			isLoadingInProgress = false;
			
			if (!isLoaded)
				module = newModuleInstance();
			
			if (moduleReadyFunction != null)
				moduleReadyFunction();
			
			new VyanaModuleEvent(VyanaModuleEvent.MODULE_READY, this).dispatch();
		}

		protected function onModuleIOError(e:IOErrorEvent):void {
			isLoadingInProgress = false;
			var f:Fault = new Fault('', e.text, e.type);
			VyanaContext.getInstance().dispatchEvent(FaultEvent.createEvent(f));
		}

		protected function onModuleError(e:ModuleEvent):void {
			isLoadingInProgress = false;
			throw new Error(e.errorText);
		}

		protected function onModuleLoadingProgress(e:ProgressEvent):void {
			VyanaContext.getInstance().dispatchEvent(new VyanaModuleProgressEvent(e.type, this, e.bubbles, e.cancelable, e.bytesLoaded, e.bytesTotal));
		}

	}
}